#include "mqttdaemon.h"
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)        
#include "qassert.h"
#endif
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QTimer>

MqttDaemon::MqttDaemon(QMap<QString,QString> necessary,QObject *parent)
    : QObject{parent}
{
    
    Q_ASSERT_X(necessary.size()>0,__func__,"necessary not null");
    Q_ASSERT_X(necessary.contains("clientId"),__func__,"Not clientId");
    m_clientId = necessary.value("clientId");
    
    Q_ASSERT_X(necessary.contains("password"),__func__,"Not password");
    m_password = necessary.value("password");
    
    Q_ASSERT_X(necessary.contains("username"),__func__,"Not username");
    m_username = necessary.value("username");
    
    Q_ASSERT_X(necessary.contains("productKey"),__func__,"Not productKey");
    m_productKey = necessary.value("productKey");
    
    Q_ASSERT_X(necessary.contains("productSecret"),__func__,"Not productSecret");
    m_productSecret = necessary.value("productSecret");
    
    Q_ASSERT_X(necessary.contains("deviceName"),__func__,"Not deviceName");
    m_deviceName = necessary.value("deviceName");
    
    Q_ASSERT_X(necessary.contains("hostname"),__func__,"Not hostname");
    m_hostname = necessary.value("hostname");
    
    Q_ASSERT_X(necessary.contains("port"),__func__,"Not port");
    m_port = necessary.value("port").toULong();
    
    initTopicMap();
}

MqttDaemon::~MqttDaemon()
{
    qDebug()<<"> "<<__func__;
}
void MqttDaemon::mqttMonitor()
{
    qDebug()<<"> "<<__func__<<__LINE__;
    if(m_mqttClient)
        return;
    m_mqttClient = new QMqttClient(this);
    m_mqttClient->setClientId(m_clientId);
    m_mqttClient->setUsername(m_username);
    m_mqttClient->setPassword(m_password);
    m_mqttClient->setHostname(m_hostname);
    m_mqttClient->setPort(m_port);
    
    connect(m_mqttClient,&QMqttClient::connected,this,&MqttDaemon::onConnected);
    connect(m_mqttClient,&QMqttClient::stateChanged,this,&MqttDaemon::onMqttState);
    m_mqttClient->connectToHost();
}

bool MqttDaemon::subscribeTheme(Topic topic)
{
    if(!m_mqttClient){
        qDebug()<<"> "<<__func__<<__LINE__<<"m_mqttClient object is null!";
        return false;
    }
        
    //OTA interface
    switch(topic){
    case Topic::OTA_sub_download_firmware:{
        QMqttSubscription * subscription = m_mqttClient->subscribe(QMqttTopicFilter(getTopic(Topic::OTA_sub_download_firmware)));
        if(!subscription){
            qCritical()<< "Failed to subscribe to topic Topic::OTA_sub_download_firmware";
            return false;
        }
        connect(subscription,&QMqttSubscription::messageReceived,this,&MqttDaemon::onMessgeReceived,Qt::UniqueConnection);
        return true;
    }break;
    case Topic::Property_sub_cloud_response:{
        QMqttSubscription * subscription = m_mqttClient->subscribe(QMqttTopicFilter(getTopic(Topic::Property_sub_cloud_response)));
        if(!subscription){
            qCritical()<< "Failed to subscribe to topic Topic::Property_sub_cloud_response";
            return false;
        }
        connect(subscription,&QMqttSubscription::messageReceived,this,&MqttDaemon::onMessgeReceived,Qt::UniqueConnection);
    }break;
    case Topic::ConfigUpdate_sub_Cloud_response:{
        QMqttSubscription * subscription = m_mqttClient->subscribe(QMqttTopicFilter(getTopic(Topic::ConfigUpdate_sub_Cloud_response)));
        if(!subscription){
            qCritical()<< "Failed to subscribe to topic Topic::ConfigUpdate_sub_Cloud_response";
            return false;
        }
        connect(subscription,&QMqttSubscription::messageReceived,this,&MqttDaemon::onMessgeReceived,Qt::UniqueConnection);
    }break;
    case Topic::Property_sub_heartReply:{
        QMqttSubscription * subscription = m_mqttClient->subscribe(QMqttTopicFilter(getTopic(Topic::Property_sub_heartReply)));
        //qDebug()<<"> subscribeTheme() heart subscribe!"<<getTopic(Topic::Property_sub_heartReply);
        if(!subscription){
            qCritical()<< "Failed to subscribe to topic Topic::Property_sub_heartResply";
            return false;
        }
        connect(subscription,&QMqttSubscription::messageReceived,this,&MqttDaemon::onMessgeReceived,Qt::UniqueConnection);
    }break;
    default:
        return false;
    }
    return true;
}

bool MqttDaemon::unsubscribeTheme(Topic topic) 
{
    if(!m_mqttClient){
        qDebug()<<"> "<<__func__<<__LINE__<<"m_mqttClient object is null!";
        return false;
    }
    
    switch(topic){
    //OTA interface
    case Topic::OTA_sub_download_firmware:{
        m_mqttClient->unsubscribe(QMqttTopicFilter(getTopic(Topic::OTA_sub_download_firmware)));
        return true;
    }
    default:
        return false;
    }
}

bool MqttDaemon::publishTheme(Topic topic,QJsonObject jsObj)
{
    if(!m_mqttClient){
        qDebug()<<"> "<<__func__<<__LINE__<<"m_mqttClient object is null!";
        return false;
    }
    QJsonDocument jsDoc;
    jsDoc.setObject(jsObj);
    QByteArray body = jsDoc.toJson();
    switch(topic){
    //OTA interface
    case Topic::OTA_pub_update_firmware:{
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::OTA_pub_update_firmware)),body);
        return !publish;
    }break;
    case Topic::OTA_pub_send_update_rate:{
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::OTA_pub_send_update_rate)),body);
        return !publish;
    }break;
    case Topic::OTA_pub_initiative_push_firmware:{
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::OTA_pub_initiative_push_firmware)),body);
        return !publish;
    }break;
    case Topic::Property_pub_device_upload:{
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::Property_pub_device_upload)),body);
        return !publish;
    }break;
    case Topic::ConfigUpdate_pub_device_item_query:{
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::ConfigUpdate_pub_device_item_query)),body);
        return !publish;
    }break;
    case Topic::Property_pub_heartRequest:{
        //qDebug()<<"> publishTheme() heart topic:"<<getTopic(Topic::Property_pub_heartRequest);
        auto publish = m_mqttClient->publish(QMqttTopicName(getTopic(Topic::Property_pub_heartRequest)),body);
        return !publish;
    }break;
    default:
        return false;
    }
}


bool MqttDaemon::isMqttConnServer()
{
    return (m_mqttClient->state() == QMqttClient::Disconnected);
}
//TODO 以后进行改进，拆包，提取有效信息，再进行信号传递
void MqttDaemon::onMessgeReceived(const QMqttMessage &msg)
{
    //qDebug()<<"> onMessgeReceived() recv info:"<<msg.payload()<<" \ntopic:"<<msg.topic();
    MqttDaemon::Topic topic = getTopicEnum(msg.topic().name());
    if(topic == Topic::Topic_unknown_theme){
        qDebug()<<"> "<<__func__<<__LINE__<<"unknown topic:"<<msg.topic().name();
        return;
    }
    
    QJsonParseError e;
    QJsonDocument jsDoc = QJsonDocument::fromJson(msg.payload(),&e);
    QJsonObject jsObj = jsDoc.object();
    switch (topic) {
    case Topic::OTA_sub_download_firmware:{
        emit downloadFirmware(jsObj);
    }
    break;
    case Topic::ConfigUpdate_sub_Cloud_response:{
        qDebug()<<"> onMessgeReceived() config update:"<<jsObj;
        emit propertyReply(jsObj);
    }break;
    case Topic::Property_sub_heartReply:{
        qDebug()<<"> onMessgeReceived() heart reply:"<<jsObj;
    }break;
    default:
        qDebug()<<"> unknow topic:"<<msg.topic();
        break;
    }
}

void MqttDaemon::onConnected()
{
    qDebug()<<"> mqtt daemon connected!";
    setMqttConnService(true);
    emit connService();
}

void MqttDaemon::onMqttState(QMqttClient::ClientState state)
{
    qDebug()<<"> MqttDaemon::onMqttState state:"<<state;
    //TODO
    if(state == QMqttClient::Disconnected){
        
        qDebug() << "MQTT client disconnected. Attempting to reconnect...";
        QTimer::singleShot(5000,[&](){
            if(m_mqttClient)
                m_mqttClient->connectToHost();
        }); // 5秒后尝试重新连接
    }
}

QString MqttDaemon::getTopic(Topic topic)
{
    return m_topicMap.value(topic);
}

MqttDaemon::Topic MqttDaemon::getTopicEnum(QString value)
{
    return m_topicMap.key(value,Topic::Topic_unknown_theme);
}

bool MqttDaemon::mqttConnService() const
{
    return m_mqttConnService;
}

void MqttDaemon::setMqttConnService(bool newMqttConnService)
{
    m_mqttConnService = newMqttConnService;
}
//大部分接口未使用，只是按照开发需求写在这里，方便以后使用；
void MqttDaemon::initTopicMap()
{
    //TODO 等需求出来了再修改
    QString tsl_event_identifier = QString();
    QString tsl_service_identifier = QString();
    QString node = "0";
    
    QString ota_update= QString("/ota/device/inform/%1/%2").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::OTA_pub_update_firmware,ota_update);
    
    QString ota_upgrade = QString("/ota/device/upgrade/%1/%2").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::OTA_sub_download_firmware,ota_upgrade);
    
    QString ota_progress = QString("/ota/device/progress/%1/%2").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::OTA_pub_send_update_rate,ota_progress);
    
    QString ota_firmware = QString("/sys/%1/%2/thing/ota/firmware/get").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::OTA_pub_initiative_push_firmware,ota_firmware);
    
    QString cs_request = QString("/ext/ntp/%1/%2/request").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::ClockSyn_pub_NTP_request,cs_request);
    
    QString cs_response = QString("/ext/ntp/%1/%2/response").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::ClockSyn_sub_NTP_response,cs_response);
    
    QString cu_push = QString("/sys/%1/%2/thing/config/push").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::ConfigUpdate_sub_cloud_downpush,cu_push);
    //TODO 还要接一下这个topic
    QString cu_get = QString("/sys/%1/%2/thing/config/get").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::ConfigUpdate_pub_device_item_query,cu_get);
    
    QString cu_get_reply = QString("/sys/%1/%2/thing/config/get_reply").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::ConfigUpdate_sub_Cloud_response,cu_get_reply);
    
    QString p_post = QString("/sys/%1/%2/thing/event/property/post").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::Property_pub_device_upload,p_post);
    //heart package
    QString cu_heart = QString("/sys/%1/%2/thing/property/post/0").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::Property_pub_heartRequest,cu_heart);
    
    QString heart = QString("/sys/%1/%2/thing/property/post_reply/0").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::Property_sub_heartReply,heart);
    
    QString p_post_reply = QString("/sys/%1/%2/thing/event/property/post_reply").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::Property_sub_cloud_response,p_post_reply);
    
    QString p_set = QString("/sys/%1/%2/thing/service/property/set").arg(m_productKey,m_deviceName);
    m_topicMap.insert(Topic::Property_sub_settings,p_set);
    
    QString eu_post = QString("/sys/%1/%2/thing/event/%3/post").arg(m_productKey,m_deviceName,tsl_event_identifier);
    m_topicMap.insert(Topic::EventUpload_pub_device,eu_post);
    
    QString eu_post_reply = QString("/sys/%1/%2/thing/event/%3/post_reply").arg(m_productKey,m_deviceName,tsl_event_identifier);
    m_topicMap.insert(Topic::EventUpload_sub_cloud_response,eu_post_reply);
    
    QString sd_service = QString("/sys/%1/%2/thing/service/%3").arg(m_productKey,m_deviceName,tsl_service_identifier);
    m_topicMap.insert(Topic::ServiceDisplay_sub_device,sd_service);
    
    QString sd_reply = QString("/sys/%1/%2/thing/service/%3/reply").arg(m_productKey,m_deviceName,tsl_service_identifier);
    m_topicMap.insert(Topic::ServiceDisplay_pub_device_response,sd_reply);
}



